home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 1996 November: Tool Chest / Dev.CD Nov 96 TC / Dev.CD Nov 96 TC.toast / Sample Code / Snippets / Sound / SndPlayDoubleBuffer / _source / Interrupt_Routines.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-05-06  |  8.0 KB  |  258 lines  |  [TEXT/CWIE]

  1. /*
  2. **    Apple Macintosh Developer Technical Support
  3. **
  4. **    Routines demonstrating how to write interrupt routines to deal with sound and I/O callbacks.
  5. **
  6. **    by Mark Cookson, Apple Developer Technical Support
  7. **
  8. **    File:    Interrupt_Routines.c
  9. **
  10. **    Copyright ©1996 Apple Computer, Inc.
  11. **    All rights reserved.
  12. **
  13. **    You may incorporate this sample code into your applications without
  14. **    restriction, though the sample code has been provided "AS IS" and the
  15. **    responsibility for its operation is 100% yours.  However, what you are
  16. **    not permitted to do is to redistribute the source as "Apple Sample
  17. **    Code" after having made changes. If you're going to re-distribute the
  18. **    source, we require that you make it clear in the source that the code
  19. **    was descended from Apple Sample Code, but that you've made changes.
  20. */
  21.  
  22. #include "Interrupt_Routines.h"
  23.  
  24. /* This gets called when the requested read has completed.
  25.    This is a good place to add any conversion routines to convert
  26.    from your sound format into the format expected by the Sound Manager.
  27.  
  28.    It's important to note that the conversions I am doing here is to help
  29.    show you how to do something more complicated.  The Sound Manager 3.2 has
  30.    an Endian converter ('sowt'), and you can specify if a sound is 'raw '
  31.    or 'twos' to have the Sound Manager do the conversion.  We do the work
  32.    ourselves for entertainment and education.
  33. */
  34. /*-----------------------------------------------------------------------*/
  35. #ifdef powerc
  36. pascal    void    ASoundFileCallBack        (myParmBlkPtr passedPB)
  37. #else
  38. pascal    void    ASoundFileCallBack        (myParmBlkPtr passedPB:__a0)
  39. #endif
  40. /*-----------------------------------------------------------------------*/
  41. {
  42.     long            i            = kInit;
  43.     myParmBlkPtr    myPB        = nil;        /* just in case a0 gets used for something else while we're here */
  44.     OSErr            theErr        = noErr;
  45.     char            dataFormat    = kInit;                    
  46.  
  47.   #ifndef powerc
  48.     long    myA5;
  49.     myA5 = SetA5 (passedPB->myA5);
  50.   #endif
  51.  
  52.     if (passedPB == nil) {
  53.         DebugPrint ("\pmyPB is nil!");
  54.         theErr = kNilPtrErr;
  55.     }
  56.     else {
  57.         myPB = passedPB;
  58.  
  59.         if (myPB->pb.ioParam.ioResult == noErr) {
  60.             if (myPB->theSoundInfo->doubleHeader.dbhSampleSize == kSixteen) {
  61.                 dataFormat += kIs16Bit;
  62.             }
  63.             if (myPB->theSoundInfo->doubleHeader.dbhNumChannels == kStereo) {
  64.                 dataFormat += kIsStereo;
  65.             }
  66.  
  67.             if (myPB->theSoundInfo->needsMasking == true) {
  68.                 for (i = kInit; i <= myPB->pb.ioParam.ioActCount / sizeof (long); i++) {
  69.                     ((long *)myPB->pb.ioParam.ioBuffer)[i] += kLongMask;    /* Convert from raw to 2's complement */
  70.                 }
  71.             }
  72.  
  73.             if (myPB->theSoundInfo->fileType == 'WAVE') {    /* We will have to rearange the data so that the Mac can play it properly */
  74.                 switch (dataFormat) {
  75.                     case kMono8Bit:        /* Do nothing */
  76.                         break;
  77.                     case kMono16Bit:    /* Do endian conversion (i.e. 0x1234 becomes 0x3412) */
  78.                             EndianMono16BitBuffer ((Ptr)myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  79.                         break;
  80.                     case kStereo8Bit:    /* Do endian conversion which takes samples {ml:mr,nl:nr} and makes them {nl:nr,ml:mr} */
  81.                             EndianStereo8BitBuffer ((Ptr)myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  82.                         break;
  83.                     case kStereo16Bit:    /* Do channel swap because 16 bit WAVEs are a channel of right then a channel of left then endian conversion */
  84.                             EndianStereo16BitBuffer ((Ptr)myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  85.                         break;
  86.                     default:
  87.                         DebugPrint ("\pBad header passed to ASoundFileCallBack");
  88.                 }
  89.             }
  90.  
  91.             if (ASoundIsBackwards (myPB->theSoundInfo) == true) {    /* reverse the sound in the buffer so it sounds like we are playing backwards */
  92.                 switch (dataFormat) {
  93.                     case kMono8Bit:
  94.                         ReverseMono8BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  95.                         break;
  96.                     case kMono16Bit:
  97.                         ReverseMono16BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  98.                         break;
  99.                     case kStereo8Bit:
  100.                         ReverseStereo8BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  101.                         break;
  102.                     case kStereo16Bit:
  103.                         ReverseStereo16BitBuffer (myPB->pb.ioParam.ioBuffer, myPB->pb.ioParam.ioActCount);
  104.                         break;
  105.                     default:
  106.                         DebugPrint ("\pBad header passed to ASoundFileCallBack");
  107.                 }
  108.                 ASoundPlayBackwards (myPB->theSoundInfo, false);
  109.             }
  110.         }
  111.         else {
  112.             DebugPrint("\pError in ASoundFileCallBack, ioResult was not zero!");
  113.             theErr = myPB->pb.ioParam.ioResult;
  114.         }
  115.     }
  116.  
  117.     if (theErr == noErr) {
  118.         myPB->theBuffer->dbFlags |= dbBufferReady;
  119.     }
  120.  
  121.   #ifndef powerc
  122.     myA5 = SetA5 (myA5);
  123.   #endif
  124.  
  125.     return;
  126. }
  127.  
  128. /* This gets called when the Sound Manager has finished playing the buffer and
  129.    wants you to refill it.
  130.  
  131.    Please note:  It may look like this routine reuses the paramBlock, but it does
  132.    not, there is a paramBlock for each buffer.  This eliminates race conditions
  133.    while priming the buffer, and makes sure that the paramBlock is not reused.
  134.  
  135.    You CANNOT reuse a paramBlock from within an ioCompletion routine!  This
  136.    will (ok, may) cause terrible problems, especially if you have File Sharing
  137.    turned on.
  138. */
  139. /*-----------------------------------------------------------------------*/
  140. pascal    void    ASoundDoubleBackProc    (SndChannelPtr chan,
  141.                                         SndDoubleBufferPtr doubleBuffer)
  142. /*-----------------------------------------------------------------------*/
  143. {
  144. #ifndef __SC__
  145. #pragma unused (chan)
  146. #endif
  147.  
  148.     SoundInfoPtr    theSoundInfo    = nil;
  149.     myParamBlockRec    *myPB            = nil;
  150.     long            bytesToCopy        = kInit;
  151.     OSErr            theErr            = noErr;
  152.  
  153.   #ifndef powerc
  154.     long    myA5;
  155.   #endif
  156.  
  157.     theSoundInfo = (SoundInfoPtr)doubleBuffer->dbUserInfo[kSndInfoPtr];
  158.     if (IsValid (theSoundInfo) == false) {
  159.         DebugPrint ("\pbad user field, dbUserInfo[0] is probably nil!");
  160.         theErr = kNilPtrErr;
  161.     }
  162.     else {
  163.         myPB = (myParmBlkPtr)doubleBuffer->dbUserInfo[kPBPtr];
  164.         if (myPB == nil) {
  165.             DebugPrint ("\pbad user field, myPB is nil!");
  166.             theErr = kNilPtrErr;
  167.         }
  168.     }
  169.  
  170.   #ifndef powerc
  171.     myA5 = SetA5 (myPB->myA5);
  172.   #endif
  173.  
  174.     if (theErr == noErr) {
  175.         myPB->pb.ioParam.ioBuffer = (Ptr)doubleBuffer->dbSoundData;
  176.  
  177.         bytesToCopy = ASoundGetNumTotalBytes(theSoundInfo) - ASoundGetBytesCopied (theSoundInfo);
  178.  
  179.         if (bytesToCopy > ASoundGetBufferSize (theSoundInfo)) {
  180.             bytesToCopy = ASoundGetBufferSize (theSoundInfo);
  181.         }
  182.  
  183.         myPB->pb.ioParam.ioReqCount = bytesToCopy;
  184.         myPB->pb.ioParam.ioPosOffset = ASoundGetBytesCopied (theSoundInfo);
  185.     
  186.         if (myPB->pb.ioParam.ioPosOffset < theSoundInfo->dataStart)    {    /* A little extra sanity checking */
  187.             myPB->pb.ioParam.ioPosOffset = theSoundInfo->dataStart;
  188.         }
  189.  
  190.         myPB->theBuffer = doubleBuffer;                /* Which buffer are we filling? */
  191.         if (bytesToCopy > kInit) {                    /* Do we really need to read more sound?*/
  192.             theErr = PBReadAsync (&myPB->pb);        /* Do an async read of more sound */
  193.         }
  194.  
  195.         if (theErr >= noErr) {
  196.             theErr = noErr;        /* positive errors are not real errors */
  197.             theSoundInfo->bytesCopied += bytesToCopy;
  198.             if (theSoundInfo->bytesPerFrame > kInit) {
  199.                 doubleBuffer->dbNumFrames = bytesToCopy / theSoundInfo->bytesPerFrame;
  200.             }
  201.             else {
  202.                 DebugPrint ("\pbytesPerFrame is a bad value!");
  203.             }
  204.  
  205.             if (theSoundInfo->bytesCopied == theSoundInfo->bytesTotal)
  206.                 doubleBuffer->dbFlags |= dbLastBuffer;
  207.     
  208.             theSoundInfo->currentBuffer++;
  209.         }
  210.         else {
  211.             DebugPrint ("\pPBReadAsync error!");
  212.         }
  213.     }
  214.  
  215.   #ifndef powerc
  216.     (void)SetA5 (myA5);
  217.   #endif
  218.  
  219.     return;
  220. }
  221.  
  222. /* This gets called when the sound is finally done playing.
  223.    It sets a flag that we check in our event loop because you can't clean up
  224.    everything at interrupt time.
  225. */
  226. /*-----------------------------------------------------------------------*/
  227. pascal    void    ASoundDoneCallBack        (SndChannelPtr chan,
  228.                                         SndCommand *cmd)
  229. /*-----------------------------------------------------------------------*/
  230. {
  231. #ifndef __SC__
  232. #ifdef powerc
  233. #pragma unused (cmd)
  234. #endif
  235. #endif
  236.  
  237.     SoundInfoPtr        theSoundInfo = nil;
  238.  
  239.   #ifndef powerc
  240.     long    myA5;
  241.     myA5 = SetA5 (cmd->param2);
  242.   #endif
  243.  
  244.     theSoundInfo = (SoundInfoPtr)chan->userInfo;
  245.     if (StrictIsValid (theSoundInfo)) {
  246.         theSoundInfo->soundDone = true;
  247.     }
  248.     else {
  249.         DebugPrint ("\pbad sound channel pointer passed to ASoundDoneCallBack");
  250.     }
  251.  
  252.   #ifndef powerc
  253.     myA5 = SetA5 (myA5);
  254.   #endif
  255.  
  256.     return;
  257. }
  258.